using System;
using System.Collections.Generic;
using NUnit.Framework;
using Tycho.Parser.Peg;

namespace Tycho.Parser.Tests.Peg {
    [TestFixture]
    public class ProductionExtensionsSpecification {
        [Test]
        public void ShouldReturnNullIfNotAllTokensAreParsed () {
            var production = new FakeParseSomeProduction (new FakeTerm (), 3);
            production.AssertNotParsed ("x y z a");
        }

        [Test]
        public void ShouldReturnNullIfTokensAreNotParsed () {
            var production = new FakeParseNoneProduction ();
            production.AssertNotParsed ("x y z a");
        }

        [Test]
        public void ShouldReturnTermIfTokensAreParsed () {
            var term = new FakeTerm ();
            var production = new FakeParseAllProduction (term);
            ITerm result = production.ParseTerm ("x y z a");
            Assert.AreSame (result, term);
        }

        [Test]
        public void ShouldReturnTermIfTokensAreParsedWithWhiteSpaceAtEnd () {
            var term = new FakeTerm ();
            var production = new FakeParseSomeProduction (term, 1);
            ITerm result = production.ParseTerm ("x ");
            Assert.AreSame (result, term);
        }

        [Test]
        public void ShouldReturnTermIfTokensAreParsedWithWhiteSpaceAndNewLinesAtEnd () {
            var term = new FakeTerm ();
            var production = new FakeParseSomeProduction (term, 1);
            ITerm result = production.ParseTerm ("x \r\n  ");
            Assert.AreSame (result, term);
        }

        class FakeTerm : Term {
            public FakeTerm () : base (null) {}
        }

        class FakeParseAllProduction : IProduction {
            private FakeTerm Term;

            public string Name { get; set; }

            public IErrorInformation ErrorInformation { get; set; }
            public Dictionary<int, ParseResult> MemoTable { get; set; }

            public FakeParseAllProduction (FakeTerm term) {
                Term = term;
            }

            public bool IsIncludedInErrors {
                get { return true; }
            }

            public bool IsInfix {
                get {
                    return false;
                }
            }

            public bool IsLeftRecursive {
                get {
                    return false;
                }
            }

            public IInfixInformation InfixInformation {
                get { return new NotInfixInformation (); }
            }

            public Dictionary<int, ParseResult> GetMemoTable (char[] source) {
                return null;
            }

            public ParseResult Parse (char[] source, int index, ParseContext context, string sourceString, ParseEnvironment parseEnvironment) {
                return new ParseResult (source.Length, Term, context);
            }

            public Yield Parse(char[] source, int index, ParseContext context, string sourceString, ParseEnvironment parseEnvironment, Func<ParseResult, Yield> continuation)
            {
                return () => continuation(Parse(source, index, context, sourceString, parseEnvironment));
            }
        }

        class FakeParseSomeProduction : IProduction {
            private FakeTerm Term;
            private readonly int UpToIndex;

            public FakeParseSomeProduction (FakeTerm term, int upToIndex) {
                Term = term;
                UpToIndex = upToIndex;
            }

            public string Name { get; set; }
            public IErrorInformation ErrorInformation { get; set; }
            public Dictionary<int, ParseResult> MemoTable { get; private set; }

            public bool IsInfix {
                get {
                    return false;
                }
            }

            public bool IsLeftRecursive {
                get {
                    return false;
                }
            }

            public IInfixInformation InfixInformation {
                get { return new NotInfixInformation (); }
            }

            public Dictionary<int, ParseResult> GetMemoTable (char [] source) {
                return null;
            }

            public bool IsIncludedInErrors { get; set; }

            public ParseResult Parse (char[] source, int index, ParseContext context, string sourceString, ParseEnvironment parseEnvironment) {
                Assert.IsTrue (source.Length > 0);

                return new ParseResult (UpToIndex, Term, context);
            }

            public Yield Parse(char[] source, int index, ParseContext context, string sourceString, ParseEnvironment parseEnvironment, Func<ParseResult, Yield> continuation)
            {
                return () => continuation(Parse(source, index, context, sourceString, parseEnvironment));
            }
        }

        class FakeParseNoneProduction : IProduction {
            public string Name { get; set; }

            public IErrorInformation ErrorInformation { get; set; }
            public Dictionary<int, ParseResult> MemoTable { get; private set; }

            public bool IsIncludedInErrors { get; set; }

            public Dictionary<int, ParseResult> GetMemoTable (char [] source) {
                return null;
            }

            public bool IsInfix {
                get {
                    return false;
                }
            }

            public bool IsLeftRecursive {
                get {
                    return false;
                }
            }

            public IInfixInformation InfixInformation {
                get { return new NotInfixInformation (); }
            }

            public ParseResult Parse (char[] source, int index, ParseContext context, string sourceString, ParseEnvironment parseEnvironment) {
                return null;
            }

            public Yield Parse(char[] source, int index, ParseContext context, string sourceString, ParseEnvironment parseEnvironment, Func<ParseResult, Yield> continuation)
            {
                return () => continuation(Parse(source, index, context, sourceString, parseEnvironment));
            }
        }
    }
}